package me.bechberger.ebpf.samples;

import me.bechberger.ebpf.annotations.Size;
import me.bechberger.ebpf.annotations.Unsigned;
import me.bechberger.ebpf.annotations.bpf.BPF;
import me.bechberger.ebpf.annotations.bpf.BPFMapDefinition;
import me.bechberger.ebpf.annotations.bpf.Type;
import me.bechberger.ebpf.bpf.BPFProgram;
import me.bechberger.ebpf.bpf.map.BPFHashMap;
import me.bechberger.ebpf.bpf.map.BPFRingBuffer;
/**
 * Adaption of {@link TypeProcessingSample} that shows how code is autogenerated
 */
@BPF(license = "GPL")
public abstract class TypeProcessingSample2 extends BPFProgram {

    private static final int FILE_NAME_LEN = 256;
    private static final int TASK_COMM_LEN = 16;

    @Type(name = "event")
    record Event(@Unsigned int pid, @Size(FILE_NAME_LEN) String filename, @Size(TASK_COMM_LEN) String comm) {}

    @BPFMapDefinition(maxEntries = 256 * 4096)
    BPFRingBuffer<Event> rb;


    static final String EBPF_PROGRAM = """
            #include "vmlinux.h"
            #include <bpf/bpf_helpers.h>
            #include <bpf/bpf_tracing.h>
            #include <string.h>
                            
            // The ebpf auto-attach logic needs the SEC
            SEC ("kprobe/do_sys_openat2")
                 int kprobe__do_sys_openat2 (struct pt_regs *ctx)
            {
              char filename[256];
              char comm[TASK_COMM_LEN] = { };
              struct event *evt;
              const char fmt_str[] = "do_sys_openat2 called by:%s file:%s pid:%d";
                            
              // Reserve the ring-buffer
              evt = bpf_ringbuf_reserve (&rb, sizeof (struct event), 0);
              if (!evt)
                {
                  return 0;
                }
              // Get the PID of the process.
              evt->pid = bpf_get_current_pid_tgid ();	// Get current process PID
                            
              // Read the filename from the second argument
              // The x86 arch/ABI have first argument in di and second in si registers (man syscall)
              bpf_probe_read (evt->filename, sizeof (filename), (char *) ctx->regs[1]);
                            
              // Read the current process name
              bpf_get_current_comm (evt->comm, sizeof (comm));
                            
              // Print a message with filename, process name, and PID
              bpf_trace_printk (fmt_str, sizeof (fmt_str), evt->comm,
                    evt->filename, evt->pid);
              // Also send the same message to the ring-buffer
              bpf_ringbuf_submit (evt, 0);
              return 0;
            }
            """;

    public static void main(String[] args) {
        try (TypeProcessingSample2 program = BPFProgram.load(TypeProcessingSample2.class)) {
            program.autoAttachProgram(program.getProgramByName("kprobe__do_sys_openat2"));
            var eventType = program.getTypeForClass(Event.class);
            program.rb.setCallback((buffer, event) -> {
                System.out.printf("do_sys_openat2 called by:%s file:%s pid:%d\n", event.comm(), event.filename(), event.pid());
            });
            while (true) {
                program.rb.consumeAndThrow();
            }
        }
    }
}